#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
import os
import math
import xml.dom.minidom
import openpyxl
from openpyxl.styles import Border, Side, PatternFill, colors, Alignment
from openpyxl.utils import get_column_letter
import resolve_svd_mo as svd_inf

reg_access_dict = {"read-write": "WR", "read-only": "RO", "write-only": "WO"}
row_height = 14
field_desp_row_len = 92
reg_desp_row_len = 123
bit_fild_row_raw_len = 2.8

border = Border(
    left=Side(style="thin", color=colors.BLACK),
    right=Side(style="thin", color=colors.BLACK),
    top=Side(style="thin", color=colors.BLACK),
    bottom=Side(style="thin", color=colors.BLACK)
)
fill_attri = PatternFill(fill_type="solid", fgColor="D9CFB1")
fill_reserve = PatternFill(fill_type="solid", fgColor="888888")
fill_reg = PatternFill(fill_type="solid", fgColor="DEECEE")

def cal_proper_height(excel_str, row_len):
    split_str = excel_str.split("\n")
    row_num = 0
    for item in split_str:
        row_num += math.ceil(len(item) / row_len)
    return (row_num + 1 )* row_height


def create_peri_addr(sheet_peri, peripherals):
    sheet_peri.cell(1, 1).value = "addr"
    sheet_peri.cell(1, 2).value = "size"
    sheet_peri.cell(1, 3).value = "peripheral"
    row_cnt = 2
    size = "0x1000"

    peri_addr_dict = {}

    for peri in  peripherals:
        name = peri.getElementsByTagName('name')[0].childNodes[0].data
        addr = peri.getElementsByTagName('baseAddress')[0].childNodes[0].data
        try:
            size = peri.getElementsByTagName('addressBlock')[0].getElementsByTagName('size')[0].childNodes[0].data
        except:
            print("peripheral: " + name + " addressBlock size is not defined")
        peri_addr_dict[addr] = name, size

    for seq_key in  sorted(peri_addr_dict) :
        sheet_peri.cell(row_cnt, 1).value = seq_key
        sheet_peri.cell(row_cnt, 2).value = peri_addr_dict[seq_key][0]
        sheet_peri.cell(row_cnt, 3).value = peri_addr_dict[seq_key][1]
        row_cnt = row_cnt + 1
    
    sheet_peri.column_dimensions['A'].width=12
    sheet_peri.column_dimensions['B'].width=9
    sheet_peri.column_dimensions['C'].width=12
    for item in range(1, 4):
        sheet_peri.cell(1, item).fill = fill_attri

    for row, row_ind in zip(sheet_peri.iter_rows(), range(1, sheet_peri.max_row + 1)):
        for cell in row:
            sheet_peri[cell.coordinate].border = border
    
    
def write_reg_map_sheet(sheet_reg_map, reg_desp_dict = {}):
    # key： cnt  value: name, offset, reset, descript, access, field_tuple
    row_cnt = 2
    bit_pos_in_sheet = 3
    bit_filed_len_list = [bit_fild_row_raw_len] * 32
    for key in  reg_desp_dict:
        #offset
        sheet_reg_map.cell(row_cnt, 1).value = reg_desp_dict[key][1] 
        sheet_reg_map.cell(row_cnt, 1).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
        #name
        sheet_reg_map.cell(row_cnt, 2).value = reg_desp_dict[key][0] 
        sheet_reg_map.cell(row_cnt, 2).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
        #reset
        sheet_reg_map.cell(row_cnt, 35).value = reg_desp_dict[key][2] 
        sheet_reg_map.cell(row_cnt, 35).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
        #field tuple
        for item in reg_desp_dict[key][5]:
            offset = item[0] 
            width = item[1][0]
            start_pos_in_sheet = bit_pos_in_sheet + 32 - (offset + width)
            filed_name = item[1][2]
            sheet_reg_map.cell(row_cnt, start_pos_in_sheet).value = filed_name
            if filed_name == 'Reserve':
                sheet_reg_map.cell(row_cnt, start_pos_in_sheet).fill = fill_reserve
            # if width == 1 and len(filed_name) * 1.2  + 0.4 > bit_filed_len_list[offset]:
            if len(filed_name) * 1.2  + 0.4 > bit_filed_len_list[offset]:
                bit_filed_len_list[offset] = len(filed_name) * 1.2  + 0.4
                # sheet_reg_map.column_dimensions[get_column_letter(3 + 31 - offset)].width=bit_filed_len_list[offset]
            sheet_reg_map.cell(row_cnt, start_pos_in_sheet).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
            sheet_reg_map.merge_cells(start_row=row_cnt, start_column=start_pos_in_sheet, end_row=row_cnt, end_column=start_pos_in_sheet+width-1)
            
        row_cnt = row_cnt + 1

    # field 的最大宽度为12 ，如果超过12，则设置为12
    max_field_len = max(bit_filed_len_list)
    if max_field_len > 12:
        max_field_len = 12
    for col in range(0, 32):
        sheet_reg_map.column_dimensions[get_column_letter(3 + 31 - col)].width = max_field_len

    for row, row_ind in zip(sheet_reg_map.iter_rows(), range(1, sheet_reg_map.max_row + 1)):
        for cell in row:
            sheet_reg_map[cell.coordinate].border = border 

def write_reg_detail_sheet(sheet_reg_detail, reg_desp_dict = {}):
    row_cnt = 1
    # row_filed_list = []
    for key in  reg_desp_dict:
        reg_str = reg_desp_dict[key][0] + ". Offset: " + reg_desp_dict[key][1] + ". Reset: " + reg_desp_dict[key][2] + "\n" + reg_desp_dict[key][3]
        field_tuple = reg_desp_dict[key][5]

        # write reg descript
        sheet_reg_detail.cell(row_cnt, 1).value = reg_str
        # sheet_reg_detail.cell(row_cnt, 1).fill = fill_reg
        sheet_reg_detail.merge_cells(start_row=row_cnt, start_column=1, end_row=row_cnt, end_column=4)
        sheet_reg_detail.cell(row_cnt, 1).alignment = Alignment(wrapText= True, horizontal='left', vertical='center')
        sheet_reg_detail.row_dimensions[row_cnt].height = cal_proper_height(reg_str, reg_desp_row_len)
        row_cnt = row_cnt + 1
    
        sheet_reg_detail.cell(row_cnt, 1).value = "Bits"
        sheet_reg_detail.cell(row_cnt, 2).value = "Name"
        sheet_reg_detail.cell(row_cnt, 3).value = "Access"
        sheet_reg_detail.cell(row_cnt, 4).value = "Description"
        sheet_reg_detail.cell(row_cnt, 1).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
        sheet_reg_detail.cell(row_cnt, 2).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
        sheet_reg_detail.cell(row_cnt, 3).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
        sheet_reg_detail.cell(row_cnt, 4).alignment = Alignment(wrapText= True, horizontal='left', vertical='center')
        sheet_reg_detail.cell(row_cnt, 1).fill = fill_reg
        sheet_reg_detail.cell(row_cnt, 2).fill = fill_reg
        sheet_reg_detail.cell(row_cnt, 3).fill = fill_reg
        sheet_reg_detail.cell(row_cnt, 4).fill = fill_reg
        sheet_reg_detail.row_dimensions[row_cnt].height = 20
        row_cnt = row_cnt + 1

        for item in field_tuple:
            sheet_reg_detail.cell(row_cnt, 1).value = item[1][1]
            sheet_reg_detail.cell(row_cnt, 2).value = item[1][2]
            sheet_reg_detail.cell(row_cnt, 3).value = item[1][3]
            sheet_reg_detail.cell(row_cnt, 4).value = item[1][4]
            sheet_reg_detail.row_dimensions[row_cnt].height = cal_proper_height(item[1][4], field_desp_row_len)
            sheet_reg_detail.cell(row_cnt, 1).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
            sheet_reg_detail.cell(row_cnt, 2).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
            sheet_reg_detail.cell(row_cnt, 3).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')
            sheet_reg_detail.cell(row_cnt, 4).alignment = Alignment(wrapText= True, horizontal='left', vertical='center')

            row_cnt = row_cnt + 1
    
    for row, row_ind in zip(sheet_reg_detail.iter_rows(), range(1, sheet_reg_detail.max_row + 1)):
        for cell in row:
            if (cell.value != None): 
                sheet_reg_detail[cell.coordinate].border = border 
 


def gen_reg_excel(svd_name, excel_name):
    DOMTree = xml.dom.minidom.parse(svd_name)
    collection = DOMTree.documentElement

    # 在集合中获取所有外设
    peripherals = collection.getElementsByTagName("peripheral")
    if os.path.exists(excel_name):
        # os.close(excel_name)
        os.remove(excel_name)

    workbook = openpyxl.Workbook()
    sheet_peri = workbook.active
    sheet_peri.title = "PERI_ADDR"
    create_peri_addr(sheet_peri, peripherals)

    for peri in  peripherals:
        name = peri.getElementsByTagName('name')[0].childNodes[0].data
        addr = peri.getElementsByTagName('baseAddress')[0].childNodes[0].data

        if peri.getElementsByTagName('register').length != 0:
            peri.getElementsByTagName("register")[0].childNodes[0].data
            reg_info_dict = svd_inf.get_peri_regs_info(peri.getElementsByTagName('register'))
        else:
            print("inherit: " + peri.getElementsByTagName("name")[0].childNodes[0].data)

        sheet_reg_map = workbook.create_sheet(name + '_map')
        sheet_reg_map.cell(1, 1).value = "Offset"
        sheet_reg_map.cell(1, 2).value = "Name"
        sheet_reg_map.column_dimensions['B'].width=17
        for bit_cnt in range(31, -1, -1):
            sheet_reg_map.cell(1, 3 + 31 - bit_cnt).value = str(bit_cnt)
            sheet_reg_map.column_dimensions[get_column_letter(3 + 31 - bit_cnt)].width=bit_fild_row_raw_len
        sheet_reg_map.cell(1, 35).value = "Reset"
        sheet_reg_map.column_dimensions['AI'].width=10.8
        for item in range(1, 36):
            sheet_reg_map.cell(1, item).fill = fill_attri
            sheet_reg_map.cell(1, item).alignment = Alignment(wrapText= True, horizontal='center', vertical='center')

        write_reg_map_sheet(sheet_reg_map, reg_info_dict)

        sheet_reg_detail = workbook.create_sheet(name + '_detail')

        sheet_reg_detail.column_dimensions['B'].width=20
        sheet_reg_detail.column_dimensions['D'].width=100
        
        write_reg_detail_sheet(sheet_reg_detail, reg_info_dict)

    workbook.save(excel_name)
